#include "stdafx.h"
#include "Server.h"
#include <Windows.h>
#include "NetworkGameMessages.h"

void Server::createServer(char* port, char* password, char* maxNumPlayers, Model *model) {
	Server::model = model;
	maxPlayers = maxNumPlayers;
	serverPort = port;
	serverPassword = password;

	server = RakNet::RakPeerInterface::GetInstance();
	server -> SetIncomingPassword(password, (int) strlen(password));
	server -> SetTimeoutTime(1000, RakNet::UNASSIGNED_SYSTEM_ADDRESS);
	
	// Can not create server
	if(!server) {
		MessageBoxA(NULL, "Failed to create server.", "test", MB_OK);
	}

	bool b = server->Startup(atoi(maxPlayers), &RakNet::SocketDescriptor(atoi(serverPort),0), 1) == RakNet::RAKNET_STARTED;
	if(!b) {
		std::cout << "Failed to start server." << std::endl;
	}

	server -> SetMaximumIncomingConnections(atoi(maxPlayers));
	server -> SetOccasionalPing(true);
	server -> SetUnreliableTimeout(2000);
	server_address = server->GetMyBoundAddress();
}

void Server::serverInfo() {
	DataStructures::List<RakNet::RakNetSmartPtr<RakNet::RakNetSocket>> sockets;
	server -> GetSockets(sockets);
	
	std::cout << "Socket adresses: " << std::endl;

	for(unsigned int i = 0; i < sockets.Size(); i++) {
		std::cout << "#: " << i << ": " << sockets[i]->boundAddress.ToString(true) << std::endl;
	}
}

void Server::receivePackets() {
	for (packet=server->Receive(); packet; server->DeallocatePacket(packet), packet=server->Receive()) {
	    
		RakNet::BitStream bsIn(packet->data,packet->length,false);
		std::list<Clients*>::iterator it;
		std::cout << "\n\nNew Packet from:" << packet->guid.g << std::endl;
		switch (packet->data[0]) {				
			case REQUEST_DISCONNECT:
				disconnectClient();
				break;
			case REQUEST_SPAWN_COMMAND:
				spawn();
				break;
			case NEW_COMMAND:
				int i, id=-1, commandType;
				bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
				bsIn.Read((char*) &i, sizeof(int));
				bsIn.Read((char*) &commandType, sizeof(int));

				if(commandType == MOVEPLAYERCOMMAND) {
					std::list<Clients*>::iterator it;
					for (it = clientList.begin() ; it != clientList.end() ; it++) {
						if(packet->systemAddress == (*it)->address) {
							int id = (*it)->id;
						}
					}

					// syncronize
					if(id != -1) {
						for (it = clientList.begin() ; it != clientList.end() ; it++) {
						//should not send to itself
							if(packet->systemAddress != (*it)->address) {
								Command* synch;
								RakNet::BitStream sync;
								RakNet::MessageID type = NEW_COMMAND;
								sync.Reset();
								sync.Write(type);
								int i;
								sync.Write((char*) &i, sizeof(int));
								synch = new SynchCommand(model->getByID(id)->getPosition(model->getByID(id)->getSceneNode()), model->getByID(id)->getSceneNode()->getOrientation(), id);
								synch->save(sync);
								server->Send(&sync, HIGH_PRIORITY,RELIABLE_ORDERED,0,(*it)->address,false);
							}
						}
					}
				}
				command(bsIn);
				break;
		}
	}
}

void Server::command(RakNet::BitStream &bsIn) {
	std::list<Clients*>::iterator it;
	for (it = clientList.begin() ; it != clientList.end() ; it++) {
		//should not send to itself
		if(packet->systemAddress != (*it)->address) {
			server->Send(&bsIn, HIGH_PRIORITY,RELIABLE_ORDERED,0,(*it)->address,false);
		}
	}
}

void Server::disconnectClient() {
	std::list<Clients*>::iterator it;
	Command* tmp; 
	int clientId = 0;
	
	// remove client
	for (it = clientList.begin() ; it != clientList.end() ; it++) {
		if(packet->systemAddress == (*it)->address) {
			clientId = (*it)->id;
			clientList.remove((*it));
			break;
		} 
	}
	
	// send disconnection command to other clients
	for (it = clientList.begin() ; it != clientList.end() ; it++) {
		tmp = new DisconnectCommand(clientId);
		type = NEW_COMMAND;
		int i = 1;
		bsOut.Reset();
		bsOut.Write(type);
		bsOut.Write((char*) &i, sizeof(int));
		tmp->save(bsOut);
		server->Send(&bsOut, HIGH_PRIORITY,RELIABLE_ORDERED,0,(*it)->address,false);
	}
}

void Server::spawn() {
	// adds a client
	int clientId = model->getNextID();
	clientList.push_back(new Clients(clientId, packet->systemAddress));

	std::list<Clients*>::iterator it;
	Command* tmp; 
	int x=0,y=0,z=0;

	// SENDING ALL OTHER CLIENTS INFORMATION TO THE CLIENT
	for (it = clientList.begin() ; it != clientList.end() ; it++) {
		if(packet->systemAddress != (*it)->address) {
			
			Ogre::Vector3 currentPos = model->getByID((*it)->id)->getPosition(model->getByID((*it)->id)->getSceneNode());
			Ogre::Quaternion orientation = model->getByID((*it)->id)->getSceneNode()->getOrientation();
			x = (int)currentPos.x;
			y = (int)currentPos.y;
			z = (int)currentPos.z;			
			tmp = new SpawnCommand(x,y,z,orientation,(*it)->id,false);

			type = NEW_COMMAND;
			int i = 1;
			bsOut.Reset();
			bsOut.Write(type);
			bsOut.Write((char*) &i, sizeof(int));
			tmp->save(bsOut);
			server->Send(&bsOut, HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false);
		} 
	}

	// SENDING THE CLIENT'S INFORMATION TO ALL OTHER CLIENTS
	for (it = clientList.begin() ; it != clientList.end() ; it++) {
		if(packet->systemAddress == (*it)->address) {
			tmp = new SpawnCommand(0,200,0,Ogre::Quaternion::IDENTITY,clientId, true);
		} else {
			tmp = new SpawnCommand(0,200,0,Ogre::Quaternion::IDENTITY,clientId, false);
			bsOut.Reset();
		} 
		bsOut.Reset();
		int i = 1;
		type = NEW_COMMAND;
		bsOut.Write(type);
		bsOut.Write((char*) &i, sizeof(int));
		tmp->save(bsOut);
		server->Send(&bsOut, HIGH_PRIORITY,RELIABLE_ORDERED,0,(*it)->address,false);
	}
}

unsigned char GetPacketIdentifier(RakNet::Packet *p)
{
	if (p==0) {
		return 255;
	}

	if ((unsigned char)p->data[0] == ID_TIMESTAMP){
		RakAssert(p->length > sizeof(RakNet::MessageID) + sizeof(RakNet::Time));
		return (unsigned char) p->data[sizeof(RakNet::MessageID) + sizeof(RakNet::Time)];
	} else {
		return (unsigned char) p->data[0];
	}
}

void Server::terminateServer() {
	RakNet::RakPeerInterface::DestroyInstance(server);
}